package com.autonavi.yunda.yunji.core.utils.datahub;



import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.autonavi.yunda.yunji.common.exception.AmapException;

import com.google.openlocationcode.OpenLocationCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.geotools.data.geojson.GeoJSONReader;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.feature.AttributeTypeBuilder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.GeoJSONUtil;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.JTS;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.feature.type.GeometryType;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;

import java.io.*;
import java.math.BigDecimal;
import java.security.InvalidParameterException;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
public class GeometryHelper {

    public static final GeometryFactory GEOMETRY_FACTORY_3857 = new GeometryFactory(new PrecisionModel(), 3857);
    public static final GeometryFactory GEOMETRY_FACTORY_4326 = new GeometryFactory(new PrecisionModel(), 4326);
    public static final double WORLD_MERC_MAX = 20037508.3427892;
    private static final String GEO_JSON_TYPE = "GeometryCollection";

    private static final String WKT_TYPE = "GEOMETRYCOLLECTION";
    public static final int GEOJSON_DECIMAL = 10;
    static GeometryJSON gjson = new GeometryJSON(GEOJSON_DECIMAL);
    public static CoordinateReferenceSystem CRS_3857 = null;
    public static CoordinateReferenceSystem CRS_4326 = null;
    public static MathTransform TRANSFORM_3857_4326 = null;
    public static MathTransform TRANSFORM_4326_3857 = null;
    private static MathTransform EquiDistant_transform;
    private static MathTransform EquiArea_Transform;

    static {
        try {
            CRS_3857 = CRS.decode("EPSG:3857", true);
            CRS_4326 = CRS.decode("EPSG:4326", true);
            TRANSFORM_3857_4326 = CRS.findMathTransform(CRS_3857, CRS_4326);
            TRANSFORM_4326_3857 = CRS.findMathTransform(CRS_4326, CRS_3857);
            CoordinateReferenceSystem equiDistant = CRS.parseWKT("PROJCS[\"Asia_North_Equidistant_Conic\",GEOGCS[\"GCS_WGS_1984\",DATUM[\"WGS_1984\",SPHEROID[\"WGS_1984\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]],PROJECTION[\"Equidistant_Conic\"],PARAMETER[\"False_Easting\",0],PARAMETER[\"False_Northing\",0],PARAMETER[\"Central_Meridian\",95],PARAMETER[\"Standard_Parallel_1\",15],PARAMETER[\"Standard_Parallel_2\",65],PARAMETER[\"Latitude_Of_Origin\",30],UNIT[\"Meter\",1],AUTHORITY[\"EPSG\",\"102026\"]]");
            EquiDistant_transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, equiDistant);
            CoordinateReferenceSystem albers = CRS.parseWKT("PROJCS[\"WGS_1984_Albers\", GEOGCS[\"WGS 84\", DATUM[\"World Geodetic System 1984\", SPHEROID[\"WGS 84\", 6378137.0, 298.257223563]], PRIMEM[\"Greenwich\", 0.0], UNIT[\"degree\", 0.017453292519943295]],  PROJECTION[\"Albers\"], PARAMETER[\"False_Easting\", 0.0], PARAMETER[\"False_Northing\", 0.0], PARAMETER[\"Central_Meridian\", 110.0], PARAMETER[\"Standard_Parallel_1\", 25.0], PARAMETER[\"Standard_Parallel_2\", 40.0],  PARAMETER[\"Latitude_Of_Origin\", 10.0],  UNIT[\"m\", 1.0]]");
            EquiArea_Transform = CRS.findMathTransform(DefaultGeographicCRS.WGS84, albers);
        } catch (FactoryException e) {
            log.error("GeometryHelper init failed,should never happen", e);
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * wkt转Json
     *
     * @param wkt
     * @return
     */
    public static JSONObject wktToJson(String wkt) {
        String json = null;
        JSONObject jsonObject = new JSONObject();
        try {
            WKTReader reader = new WKTReader();
            Geometry geometry = reader.read(wkt);
            StringWriter writer = new StringWriter();
            GeometryJSON geometryJSON = new GeometryJSON(GEOJSON_DECIMAL);
            geometryJSON.write(geometry, writer);

            json = writer.toString();
            jsonObject = JSONObject.parseObject(json);

        } catch (Exception e) {
//            log.error("wkt不合法", e);
            throw new AmapException(e.getMessage() + ",input geometry is " + wkt);
        }
        return jsonObject;
    }

    /**
     * geoJson转wkt
     *
     * @param jsonObject
     * @return
     */
    public static String jsonToWkt(JSONObject jsonObject) {
        String wkt = null;
        String type = jsonObject.getString("type");
        GeometryJSON gJson = new GeometryJSON(GEOJSON_DECIMAL);
        try {
            // {"geometries":[{"coordinates":[4,6],"type":"Point"},{"coordinates":[[4,6],[7,10]],"type":"LineString"}],"type":"GeometryCollection"}
            if (GEO_JSON_TYPE.equals(type)) {
                // 由于解析上面的json语句会出现这个geometries属性没有采用以下办法
                JSONArray geometriesArray = jsonObject.getJSONArray("geometries");
                // 定义一个数组装图形对象
                int size = geometriesArray.size();
                Geometry[] geometries = new Geometry[size];
                for (int i = 0; i < size; i++) {
                    String str = geometriesArray.get(i).toString();
                    // 使用GeoUtil去读取str
                    Reader reader = GeoJSONUtil.toReader(str);
                    org.locationtech.jts.geom.Geometry geometry = gJson.read(reader);
                    geometries[i] = geometry;
                }
                GeometryCollection geometryCollection = new GeometryCollection(geometries, new GeometryFactory());
                wkt = geometryCollection.toText();
            } else {
                Reader reader = GeoJSONUtil.toReader(jsonObject.toString());
                Geometry read = gJson.read(reader);
                wkt = read.toText();
            }


        } catch (Exception e) {
//            log.error("wkt不合法", e);
            throw new AmapException(e.getMessage() + ",input geometry is " + jsonObject.toJSONString());
        }
        return wkt;
    }

    public static Geometry wktToGeometry(String wkt) {
        WKTReader reader = new WKTReader();
        Geometry geometry = null;
        try {
            geometry = reader.read(wkt);
        } catch (Exception e) {
//            log.error("wkt不合法", e);
            throw new AmapException(e.getMessage() + ",input geometry is " + wkt);
        }
        return geometry;
    }

    /**
     * jtsGeometry转wkt
     *
     * @param geometry
     * @return
     */
    public static String geometryToWkt(Geometry geometry) {
        WKTWriter writer = new WKTWriter();
        return writer.write(geometry);
    }

    /**
     * jts geometry 转 geojson
     *
     * @param geometry
     * @return
     */
    public static String geometryToGeojson(Geometry geometry) {
        final StringWriter stringWriter = new StringWriter();
        try {
            gjson.write(geometry, stringWriter);
        } catch (IOException e) {
            throw new AmapException(e.getMessage() + "should never happen ");
        }
        return stringWriter.toString();
    }

    /**
     * jts geometry 转 geojson jsonobject
     *
     * @param geometry
     * @return
     */
    public static JSONObject geometryToGeojsonObject(Geometry geometry) {
        final StringWriter stringWriter = new StringWriter();
        try {
            gjson.write(geometry, stringWriter);
        } catch (IOException e) {
            throw new AmapException(e.getMessage() + "should never happen ");
        }
        return JSON.parseObject(stringWriter.toString());
    }

    /**
     * geoJson转jts geometry
     *
     * @param jsonObject
     * @return
     */
    public static Geometry geojsonToGeometry(JSONObject jsonObject) {
        String type = jsonObject.getString("type");
        GeometryJSON gJson = new GeometryJSON(GEOJSON_DECIMAL);
        try {
            if (GEO_JSON_TYPE.equals(type)) {
                // 由于解析上面的json语句会出现这个geometries属性没有采用以下办法
                JSONArray geometriesArray = jsonObject.getJSONArray("geometries");
                // 定义一个数组装图形对象
                int size = geometriesArray.size();
                Geometry[] geometries = new Geometry[size];
                for (int i = 0; i < size; i++) {
                    String str = geometriesArray.get(i).toString();
                    // 使用GeoUtil去读取str
                    Reader reader = GeoJSONUtil.toReader(str);
                    org.locationtech.jts.geom.Geometry geometry = gJson.read(reader);
                    geometries[i] = geometry;
                }
                return new GeometryCollection(geometries, new GeometryFactory());
            } else {
                final Set<String> keySet = jsonObject.keySet();
                if (keySet.size() > 2) {
                    //证明有除了type\coordinates之外的其他key，需要过滤，否则如果里面有其他数组，就会触发转换出来多一个empty的ring
                    return gJson.read(GeoJSONUtil.toReader(getStandard(jsonObject).toString()));
                }
                return gJson.read(GeoJSONUtil.toReader(jsonObject.toString()));
            }
        } catch (Exception e) {
            throw new AmapException(e.getMessage() + ",input geometry is " + jsonObject.toJSONString());
        }
    }

    private static JSONObject getStandard(JSONObject jsonObject) {
        final JSONObject standard = new JSONObject();
        standard.put("type", jsonObject.get("type"));
        standard.put("coordinates", jsonObject.get("coordinates"));
        return standard;
    }

    /**
     * 根据x,y,z计算web mercator bbox.
     *
     * @param x 横坐标
     * @param y 纵坐标
     * @param z zoom等级
     * @return web mercator bbox.
     */
    public static Envelope xyzToEnvelope(int x, int y, int z) {
//         Width of world in EPSG:3857
        double worldMercMin = -1 * WORLD_MERC_MAX;
        double worldMercSize = WORLD_MERC_MAX - worldMercMin;
//        Width in tiles
        double worldTileSize = Math.pow(2, z);
//        # Tile width in EPSG:3857
        double tileMercSize = worldMercSize / worldTileSize;
        double xmin = worldMercMin + tileMercSize * x;
        double xmax = worldMercMin + tileMercSize * (x + 1);
        double ymin = WORLD_MERC_MAX - tileMercSize * (y + 1);
        double ymax = WORLD_MERC_MAX - tileMercSize * y;
        Envelope env = new Envelope(xmin, xmax, ymin, ymax);
        return env;
    }

    /**
     * 扩展webmercator bbox，为了解决原大小过滤数据时 可能引起前端mvt显示不完整的问题
     * 在原有的宽度和高度上扩展0.2倍
     *
     * @return web mercator bbox.
     */
    public static Envelope expandEnvelope(Envelope envelope) {
        final double absx = Math.abs(envelope.getMaxX() - envelope.getMinX()) * 0.2;
        final double absy = Math.abs(envelope.getMaxY() - envelope.getMinY()) * 0.2;
        double xmin = Math.max(-WORLD_MERC_MAX, envelope.getMinX() - absx);
        double xmax = Math.min(WORLD_MERC_MAX, envelope.getMaxX() + absx);
        double ymin = Math.max(-WORLD_MERC_MAX, envelope.getMinY() - absy);
        double ymax = Math.min(WORLD_MERC_MAX, envelope.getMaxY() + absx);
        return new Envelope(xmin, xmax, ymin, ymax);
    }

    /**
     * 根据x,y,z计算web mercator bbox.
     * 转换成4326坐标系的polygon
     *
     * @param x 横坐标
     * @param y 纵坐标
     * @param z zoom等级
     * @return 4326 Polygon.
     */
    public static Geometry xyzToPolygon(int x, int y, int z) {
//         Width of world in EPSG:3857
        double worldMercMax = 20037508.3427892;
        double worldMercMin = -1 * worldMercMax;
        double worldMercSize = worldMercMax - worldMercMin;
//        Width in tiles
        double worldTileSize = Math.pow(2, z);
//        # Tile width in EPSG:3857
        double tileMercSize = worldMercSize / worldTileSize;
        double xmin = worldMercMin + tileMercSize * x;
        double xmax = worldMercMin + tileMercSize * (x + 1);
        double ymin = worldMercMax - tileMercSize * (y + 1);
        double ymax = worldMercMax - tileMercSize * y;
        Envelope env = new Envelope(xmin, xmax, ymin, ymax);
        try {
            env = JTS.transform(env, TRANSFORM_3857_4326);
//            final Coordinate coordinate1 = JTS.transform(new CoordinateXY(ymin, xmin), null, TRANSFORM_3857_4326);
//            final Coordinate coordinate2 = JTS.transform(new CoordinateXY(ymax, xmin), null, TRANSFORM_3857_4326);
//            final Coordinate coordinate3 = JTS.transform(new CoordinateXY(ymax, xmax), null, TRANSFORM_3857_4326);
//            final Coordinate coordinate4 = JTS.transform(new CoordinateXY(ymin, xmax), null, TRANSFORM_3857_4326);
//            final Coordinate[] coordinates = {
//                    coordinate1,
//                    coordinate2,
//                    coordinate3,
//                    coordinate4,
//                    coordinate1
//            };
//            return GEOMETRY_FACTORY_3857.toGeometry(env);
            return GEOMETRY_FACTORY_4326.toGeometry(env);
//            return GEOMETRY_FACTORY_4326.createPolygon(coordinates);
        } catch (Exception e) {
            throw new AmapException(e.getMessage() + ",input x=" + x + ",y=" + y + ",z=" + z);
        }
    }

    /**
     * 把envelope转换为double数组
     *
     * @param envelope
     * @return
     */
    public static double[] envelopeToDoubleArray(Envelope envelope) {
        if (envelope != null) {
            return new double[]{envelope.getMinX(), envelope.getMinY(), envelope.getMaxX(), envelope.getMaxY()};
        } else {
            return new double[]{};
        }
    }

    /**
     * 经度和纬度用,分割，经度在前，纬度在后，坐标对用|分割。经纬度小数点后不得超过6位。
     * 多边形为矩形时，可传入左上右下两顶点坐标对；其他情况下首尾坐标对需相同
     * 例子：  0,0|6,0|6,6|0,6|0,0  或者 0,0|6,6
     *
     * @param seperatedCoordinates
     * @return
     */
    public static Polygon buildPolygon(String seperatedCoordinates) {
        final String[] split = StringUtils.split(seperatedCoordinates, '|');
        if (split == null || split.length < 2 || split.length == 3) {
            //如果入参格式不对,错误处理参考开平
            throw new InvalidParameterException("点数量不足");
        }
        if (split.length > 2) {
            //2个点以上的，必须首尾相同
            if (!StringUtils.equals(split[0], split[split.length - 1])) {
                throw new InvalidParameterException("必须首尾相同");
            }
        }
        List<Coordinate> coordinates = new ArrayList<>();
        for (String lanlat : split) {
            final String[] lonLat = StringUtils.split(lanlat, ",");
            if (lonLat != null && lonLat.length == 2) {
                double lon = Double.parseDouble(lonLat[0]);
                double lat = Double.parseDouble(lonLat[1]);
                coordinates.add(new Coordinate(lon, lat));
            } else {
                throw new InvalidParameterException();
            }
        }
        if (coordinates.size() == 2) {
            //如果只有两个点，尝试转换成正方形。
            final double x1 = coordinates.get(0).getX();
            final double y1 = coordinates.get(0).getY();
            final double x2 = coordinates.get(1).getX();
            final double y2 = coordinates.get(1).getY();
            if (Double.compare(x1, x2) == 0 || Double.compare(y1, y2) == 0) {
                throw new AmapException("传入的两点不能构成正方形");
            }
            //清除掉，重新按顺序添加
            coordinates.clear();
            coordinates.add(new Coordinate(x1, y1));
            coordinates.add(new Coordinate(x1, y2));
            coordinates.add(new Coordinate(x2, y2));
            coordinates.add(new Coordinate(x2, y1));
            coordinates.add(new Coordinate(x1, y1));
        }

        return GEOMETRY_FACTORY_4326.createPolygon(coordinates.toArray(new Coordinate[0]));
    }

    /**
     * 经度和纬度用,分割，经度在前，纬度在后，坐标对用|分割。经纬度小数点后不得超过6位。
     * 多边形为矩形时，可传入左上右下两顶点坐标对；其他情况下首尾坐标对需相同
     * 例子：  0,0|6,0|6,6|0,6|0,0  或者 0,0|6,6
     *
     * @param seperatedCoordinates
     * @return
     */
    public static String buildPolygonToWkt(String seperatedCoordinates) {
        return buildPolygon(seperatedCoordinates).toText();
    }


    /**
     * 根据西南角和东北角构造一个长方形polygon
     *
     * @param westLon  西边经度
     * @param southLat 南边纬度
     * @param eastLon  东边经度
     * @param northLat 北边纬度
     * @return
     */
    public static Polygon buildPolygon(double westLon, double southLat, double eastLon, double northLat) {
        if (!longitudeIsValid(westLon) || !latitudeIsValid(southLat) || !longitudeIsValid(eastLon) || !latitudeIsValid(northLat)) {
            throw new InvalidParameterException();
        }
        Coordinate[] coordinates = new Coordinate[5];
        coordinates[0] = new Coordinate(westLon, southLat);
        coordinates[1] = new Coordinate(westLon, northLat);
        coordinates[2] = new Coordinate(eastLon, northLat);
        coordinates[3] = new Coordinate(eastLon, southLat);
        coordinates[4] = new Coordinate(westLon, southLat);

        return GEOMETRY_FACTORY_4326.createPolygon(coordinates);
    }

    /**
     * 中心点坐标，规则：经度和纬度用","分割，经度在前，纬度在后，经纬度小数点后不得超过6位
     * 例子 2,2
     * 坐标系 4326
     *
     * @param seperatedCoordinate
     * @return
     */
    public static Point buildPoint(String seperatedCoordinate) {
        // 转换成点
        final String[] lonLat = StringUtils.split(seperatedCoordinate, ",");
        if (lonLat == null || lonLat.length != 2) {
            //如果入参格式不对,错误处理参考开平
            throw new InvalidParameterException();
        }
        Coordinate coordinate;
        try {
            double lon = Double.parseDouble(lonLat[0]);
            double lat = Double.parseDouble(lonLat[1]);
            coordinate = new Coordinate(lon, lat);
        } catch (Exception e) {
            throw new InvalidParameterException();
        }

        return GEOMETRY_FACTORY_4326.createPoint(coordinate);
    }

    /**
     * 中心点坐标，规则：经度和纬度用","分割，经度在前，纬度在后，经纬度小数点后不得超过6位
     * 例子 2,2
     * 坐标系 4326
     *
     * @param seperatedCoordinate
     * @return
     */
    public static String buildPointToWkt(String seperatedCoordinate) {
        return buildPoint(seperatedCoordinate).toText();
    }



    /**
     * 中心点坐标，规则：经度和纬度用","分割，经度在前，纬度在后，经纬度小数点后不得超过6位
     * 例子 2,2
     * 坐标系 4326
     *
     * @param lon 经度
     * @param lat 纬度
     * @return
     */
    public static Point buildPoint(double lon, double lat) {
        if (!longitudeIsValid(lon) || !latitudeIsValid(lat)) {
            //如果入参格式不对,错误处理参考开平
            throw new InvalidParameterException();
        }
        Coordinate coordinate = new Coordinate(lon, lat);

        return GEOMETRY_FACTORY_4326.createPoint(coordinate);
    }



    private static boolean longitudeIsValid(double lon) {
        return lon <= 180 && lon >= -180;
    }

    private static boolean latitudeIsValid(double lat) {
        return lat <= 90 && lat >= -90;
    }

    /**
     * 精确计算平面长度，单位米
     *
     * @param geo 几何对象
     * @return
     * @author mike
     */
    public static double getLength4Geom(Geometry geo) {
        try {
            geo = JTS.transform(geo, EquiDistant_transform);
        } catch (org.opengis.referencing.operation.TransformException transformException) {
            transformException.printStackTrace();
        }
        return geo.getLength();
    }

    /**
     * 精确计算平面面积，单位平方米
     *
     * @param geo
     * @return
     * @author mike
     */
    public static double getArea4Geom(Geometry geo) {
        try {
            geo = JTS.transform(geo, EquiArea_Transform);
        } catch (org.opengis.referencing.operation.TransformException transformException) {
            transformException.printStackTrace();
        }
        double area = geo.getArea();
        return BigDecimal.valueOf(area).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }
//
//    /**
//     * geojson文件转化为shapefile
//     *
//     * @param geojson
//     * @param file
//     * @throws IOException
//     */
//    public static void geojsonToShapeFile(String geojson, File file) throws IOException {
//        //把文件导出到一个指定的文件夹下，然后压缩成zip文件
//        final String zipPath = (file.getParent()) + "/" + StringUtils.substringBeforeLast(file.getName(), ".");
//        final Path tempDir = Paths.get(zipPath + "/");
//        Files.createDirectories(tempDir);
//        ShapefileDumper dumper = new ShapefileDumper(tempDir.toFile());
//        // split when shp or dbf reaches 100MB
//        int maxSize = 100 * 1024 * 1024;
//        dumper.setMaxDbfSize(maxSize);
//        dumper.setMaxDbfSize(maxSize);
//        dumper.setCharset(StandardCharsets.UTF_8);
//        // actually dump data
////        FeatureCollection fc = convertToFeatureCollection(geojson);
////        SimpleFeatureCollection sfc = DataUtilities.collection(fc);
//        final SimpleFeatureCollection sfc = parseFeatureCollection(geojson);
//        try {
//            // if an empty result out of feature type with unknown geometry is created, the
//            // zip file will be empty and the zip output stream will break
//            dumper.dump(sfc);
//
//            // zip all the files produced
//            try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(file))) {
//                IOUtils.zipDirectory(tempDir.toFile(), zipOut, (dir, name) -> true);
//                zipOut.finish();
//            }
//        } catch (Exception e) {
//            log.error("dump shapefile failed", e);
//            throw e;
//        } finally {
//            // make sure we remove the temp directory and its contents completely now
//            try {
//                FileUtils.deleteDirectory(tempDir.toFile());
//            } catch (IOException e) {
//                log.error("cleaning tempdir failed", e);
//            }
//        }
//    }

    /**
     * Parses and returns a feature collection from a GeoJSON
     */
    public static SimpleFeatureCollection parseFeatureCollection(String jsonString) {
        try (GeoJSONReader reader =
                     new GeoJSONReader(new ByteArrayInputStream(jsonString.getBytes()))) {
            SimpleFeatureCollection features = (SimpleFeatureCollection) reader.getFeatures();
            return features;
        } catch (IOException e) {
            throw new RuntimeException("problem parsing FeatureCollection", e);
        }
    }

    private static FeatureCollection convertToFeatureCollection(String geojson) throws IOException {
        GeometryJSON gjson = new GeometryJSON(GEOJSON_DECIMAL);
        FeatureJSON fjson = new FeatureJSON(gjson);
        SimpleFeatureType simpleFeatureType = fjson.readFeatureCollectionSchema(geojson, false);
        SimpleFeatureTypeBuilder simpleFeatureTypeBuilder = new SimpleFeatureTypeBuilder();
        final List<AttributeDescriptor> attributeDescriptors = simpleFeatureType.getAttributeDescriptors();
        simpleFeatureTypeBuilder.setName("feature");
        AttributeTypeBuilder build = new AttributeTypeBuilder();
        build.setNillable(true);
        build.setCRS(CRS_4326);
        build.setBinding(Geometry.class);
        GeometryType geometryType = build.buildGeometryType();
        GeometryDescriptor geometryDescriptor = build.buildDescriptor("geometry", geometryType);
        simpleFeatureTypeBuilder.add(geometryDescriptor);
        for (AttributeDescriptor attributeDescriptor : attributeDescriptors) {
            if (!attributeDescriptor.getLocalName().equals("geometry")) {
                simpleFeatureTypeBuilder.add(attributeDescriptor);
            }
        }
        //统一设置为crs 4326
        simpleFeatureTypeBuilder.setCRS(CRS_4326);
        simpleFeatureType = simpleFeatureTypeBuilder.buildFeatureType();
        fjson.setFeatureType(simpleFeatureType);
        FeatureCollection fc = fjson.readFeatureCollection(geojson);
        return fc;
    }

    public static Geometry fastJsonToJts(com.alibaba.fastjson.support.geo.Geometry geometry) {
        if (geometry == null) {
            return null;
        }
        return geojsonToGeometry((JSONObject) JSON.toJSON(geometry));
    }

    public static Polygon fastJsonToJts(com.alibaba.fastjson.support.geo.Polygon geometry) {
        if (geometry == null) {
            return null;
        }
        return (Polygon) geojsonToGeometry((JSONObject) JSON.toJSON(geometry));
    }

    /**
     * 开平的行政区polyline转换为多面
     *
     * @param polyline 开平格式
     * @return 多面
     */
    public static MultiPolygon restAPIPolyline2MultiPolygon(String polyline) {
        if (StringUtils.isEmpty(polyline)) {
            throw new InvalidParameterException();
        }
        GeometryFactory gf = new GeometryFactory();
        List<Polygon> polygons = new ArrayList<>(2);
        try {
            StringTokenizer st = new StringTokenizer(polyline, "|");
            while (st.hasMoreTokens()) {
                String line = st.nextToken();
                boolean isx = true;
                CoordinateXY cs = null;
                List<CoordinateXY> coors = new ArrayList<>(128);
                StringTokenizer xys = new StringTokenizer(line, ";,");
                while (xys.hasMoreTokens()) {
                    if (isx) {
                        cs = new CoordinateXY();
                        cs.setOrdinate(CoordinateXY.X, Double.parseDouble(xys.nextToken()));
                    } else {
                        cs.setOrdinate(CoordinateXY.Y, Double.parseDouble(xys.nextToken()));
                        coors.add(cs);
                    }
                    isx = !isx;
                }
                polygons.add(gf.createPolygon(coors.toArray(new Coordinate[0])));
            }
        } catch (Exception e) {
            throw new InvalidParameterException();
        }
        return gf.createMultiPolygon(polygons.toArray(new Polygon[0]));
    }

    public static Map<String, Object> featureWktEntityToGeojsonMap(FeatureWkt featureWkt) {
        if (featureWkt == null) {
            return null;
        }
        Map<String, Object> geojson = new HashMap<>();
        geojson.put("type", "Feature");
        geojson.put("geometry", GeometryHelper.geometryToGeojsonObject(featureWkt.getGeometry()));
        geojson.put("properties", featureWkt.getProperties());
        return geojson;
    }

    public static Map<String, Object> featureWktEntityToGeojsonMap(FeatureWkt featureWkt, Set<String> notNeedProps) {
        if (featureWkt == null) {
            return null;
        }
        Map<String, Object> geojson = new HashMap<>();
        geojson.put("type", "Feature");
        geojson.put("geometry", GeometryHelper.geometryToGeojsonObject(featureWkt.getGeometry()));
        Map<String, Object> properties = featureWkt.getProperties();
        Map<String, Object> newProperties = new HashMap<>();
        if (properties != null && notNeedProps != null && !notNeedProps.isEmpty()) {
            //需要过滤
            for (String key : properties.keySet()) {
                if (!notNeedProps.contains(key)) {
                    newProperties.put(key, properties.get(key));
                }
            }
            geojson.put("properties", newProperties);
        } else {
            geojson.put("properties", properties);
        }
        return geojson;
    }

    public static String convertToGeojsonFeatureCollection(List<FeatureWkt> list) {
        Map<String, Object> featureCollection = new HashMap<>();
        featureCollection.put("type", "FeatureCollection");
        featureCollection.put("features", list.stream().map(GeometryHelper::featureWktEntityToGeojsonMap).collect(Collectors.toList()));
        return JSON.toJSONString(featureCollection);
    }

    /**
     * 把点坐标编码成OLC,编码位数为10（+不计算）
     *
     * @param point 点 WGS84坐标系
     * @return
     */
    public static String encodeOLC(Point point) {
        return encodeOLC(point.getX(), point.getY(), 10);
    }

    /**
     * 把点坐标编码成OLC，结果长度可以指定（+不计算）
     *
     * @param point  点 WGS84坐标系
     * @param length 编码位数 最小4 最大15
     * @return
     */
    public static String encodeOLC(Point point, int length) {
        return encodeOLC(point.getX(), point.getY(), length);
    }

    /**
     * 把点坐标编码成OLC
     *
     * @param lon    经度
     * @param lat    纬度
     * @param length 长度
     * @return
     */
    public static String encodeOLC(double lon, double lat, int length) {
        try {
            return OpenLocationCode.encode(lat, lon, length);
        } catch (Exception e) {
            throw new AmapException("OpenLocationCode encode error");
        }
    }

    /**
     * 把OLC解码为多边形
     *
     * @param pluscode OLC 必须一个完整的合法olc 否则报错
     * @return
     */
    public static Polygon decodeOLC(String pluscode) {
        try {

            final OpenLocationCode.CodeArea decode = OpenLocationCode.decode(pluscode);
            final double westLongitude = decode.getWestLongitude();
            final double southLatitude = decode.getSouthLatitude();
            final double northLatitude = decode.getNorthLatitude();
            final double eastLongitude = decode.getEastLongitude();
            return buildPolygon(westLongitude, southLatitude, eastLongitude, northLatitude);
        } catch (Exception e) {
            throw new AmapException("OpenLocationCode decode error");
        }
    }

    /**
     * @param list         原数据
     * @param notNeedProps 不需要的属性 为null或空，则不需要处理；不为空则需要过滤掉
     * @return
     */
    public static String convertToGeojsonFeatureCollection(List<FeatureWkt> list, Set<String> notNeedProps) {
        Map<String, Object> featureCollection = new HashMap<>();
        featureCollection.put("type", "FeatureCollection");
        featureCollection.put("features", list.stream().map(t -> GeometryHelper.featureWktEntityToGeojsonMap(t, notNeedProps)).collect(Collectors.toList()));
        return JSON.toJSONString(featureCollection);
    }
}
